home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Tool Chest / QuickDraw / Virtual Sphere 1.0.1 / Virtual Sphere Sample Code 1.1 / Graphics3D.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-25  |  14.0 KB  |  444 lines  |  [TEXT/MPS ]

  1. /*•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  2. /* Graphics3D.c
  3. /*
  4. /* 3D graphics and math routines
  5. /*
  6. /* Author: Michael Chen, Human Interface Group / ATG
  7. /* Copyright © 1991-1993 Apple Computer, Inc.  All rights reserved.
  8. /*
  9. /* Part of Virtual Sphere Sample Code Release v1.1
  10. /*•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••*/
  11.  
  12. #ifndef    __GRAPHICS3D__
  13. #include "Graphics3D.h"
  14. #endif
  15.  
  16. #ifndef __FIXMATH__
  17. #include <FixMath.h>
  18. #endif
  19.  
  20. #ifndef __LIMITS__
  21. #include <Limits.h>
  22. #endif
  23.  
  24. #ifndef __RESOURCES__
  25. #include <Resources.h>
  26. #endif
  27.  
  28. #ifndef __MEMORY__
  29. #include <Memory.h>
  30. #endif
  31.  
  32. #ifndef __ERRORS__
  33. #include <Errors.h>
  34. #endif
  35.  
  36. #include "Sample.h"
  37.  
  38. /* Structure used for dithering patterns */
  39. typedef struct {
  40.     short    patListSize;    /* Size */
  41.     Pattern    patList[1];        /* Pointer to varible size arrary */
  42. } **PatListHandle;
  43.  
  44. /* Local globals for this file (module) only */
  45. static ConstPatternParam    lgPolyShade;
  46. static PatListHandle        lgDitherPatterns = nil;
  47.  
  48. /* Local routine */
  49. static unsigned long RGBToGrayscale (const RGBColor *rGBColor, Integer maxGrayValue);
  50.  
  51. /*=================================================================================================
  52. /* InitializeGraphics3D
  53. /*-------------------------------------------------------------------------------------------------*/
  54. pascal OSErr InitializeGraphics3D ()
  55. {
  56.     OSErr err;
  57.     
  58.     /* Load in dither patterns for black and white drawing. */
  59.     if (lgDitherPatterns == nil) {
  60.         lgDitherPatterns = (PatListHandle) GetResource ('PAT#', rDitherPatterns);
  61.         if (lgDitherPatterns == nil) return (resNotFound);
  62.  
  63.         /* Move the dithering pattern as high up in memory as possible and 
  64.          * and lock it down.  */
  65.         DetachResource ((Handle) lgDitherPatterns);    if ((err = MemError()) != noErr) return (err);
  66.         MoveHHi        ((Handle) lgDitherPatterns);    if ((err = MemError()) != noErr) return (err);
  67.         HNoPurge       ((Handle) lgDitherPatterns);    if ((err = MemError()) != noErr) return (err);
  68.         HLock          ((Handle) lgDitherPatterns);    if ((err = MemError()) != noErr) return (err);
  69.     }
  70.     return (noErr);
  71. }
  72.  
  73. /*=================================================================================================
  74. /* FreeGraphics3D
  75. /*-------------------------------------------------------------------------------------------------*/
  76. pascal void FreeGraphics3D ()
  77. {
  78.     /* Dispose the memeory used for the dither patterns  */
  79.     if (lgDitherPatterns == nil) {
  80.         DisposeHandle ((Handle) lgDitherPatterns);
  81.     }
  82. }
  83.  
  84. /*=================================================================================================
  85. /* CopyMatrix
  86. /*-------------------------------------------------------------------------------------------------*/
  87. typedef struct {        /* Struct to make matrix copying more efficient */
  88.     Matrix4D a;
  89. } MatrixAsStruct;
  90.  
  91. pascal void CopyMatrix (Matrix4D fromMatrix, Matrix4D toMatrix)
  92. {
  93.     * (MatrixAsStruct *) toMatrix = * (MatrixAsStruct *) fromMatrix;
  94. }
  95.  
  96. /*=================================================================================================
  97. /* CrossProduct3D
  98. /*
  99. /* Returns the right-handed cross-product of a and b in c
  100. /*-------------------------------------------------------------------------------------------------*/
  101. pascal void CrossProduct3D (const CPoint3D *a, const CPoint3D *b, CPoint3D *aCrossB)
  102. {
  103.     aCrossB->x = a->y * b->z - a->z * b->y;
  104.     aCrossB->y = a->z * b->x - a->x * b->z;
  105.     aCrossB->z = a->x * b->y - a->y * b->x;
  106. }
  107.  
  108. /*=================================================================================================
  109. /* DotProduct3D
  110. /*
  111. /* Returns the dot-product of a and b
  112. /*-------------------------------------------------------------------------------------------------*/
  113. pascal Real DotProduct3D (const CPoint3D *a, const CPoint3D *b)
  114. {
  115.     return (a->x*b->x + a->y*b->y + a->z*b->z);
  116. }
  117.  
  118. /*=================================================================================================
  119. /* DrawPolyNet
  120. /*
  121. /* Draws a PolyNet object with different backfaced polygon removal and rendering options.
  122. /*-------------------------------------------------------------------------------------------------*/
  123. pascal void DrawPolyNet (const PolygonNetData *polyNet)
  124. {
  125.     Point3D        *vertices;            /* local index into the vertex array */
  126.     Integer        *vertexIndices;
  127.     Integer        i;
  128.     
  129.     if (polyNet->fVertexCount < 0) DebugMessage ("\pfVertexCount < 0");
  130.  
  131.     vertices = polyNet->fVertices;
  132.     vertexIndices = polyNet->fVertexIndices;
  133.     
  134.     /* For each polygon... */
  135.     for (i = 0; i < polyNet->fPolygonCount; i++) {
  136.         Integer firstVertexIndex;
  137.         NetPolygon *thisPoly = &polyNet->fPolys[i];
  138.         if (thisPoly->fVertexCount < 3) continue;    /* This is not a polygon.  Skip to next one. */
  139.         firstVertexIndex = thisPoly->fFirstVertexIndex;
  140.  
  141.         if (gDoBackfacedPolygonRemoval) {
  142.             /* Transform first 3 points to screen coordinate to do backface polygon test.
  143.              * Code will not work if any vertex is offscreen!
  144.              */
  145.             Point p0, p1, p2;
  146.             Integer v1h, v1v, v2h, v2v;
  147.             
  148.             MoveTo3D (vertices [vertexIndices [firstVertexIndex  ]].x,
  149.                       vertices [vertexIndices [firstVertexIndex  ]].y,
  150.                       vertices [vertexIndices [firstVertexIndex  ]].z);
  151.             GetPen (&p0);
  152.             MoveTo3D (vertices [vertexIndices [firstVertexIndex+1]].x,
  153.                       vertices [vertexIndices [firstVertexIndex+1]].y,
  154.                       vertices [vertexIndices [firstVertexIndex+1]].z);
  155.             GetPen (&p1);
  156.             MoveTo3D (vertices [vertexIndices [firstVertexIndex+2]].x,
  157.                       vertices [vertexIndices [firstVertexIndex+2]].y,
  158.                       vertices [vertexIndices [firstVertexIndex+2]].z);
  159.             GetPen (&p2);
  160.  
  161.             v1h = p1.h - p0.h;
  162.             v1v = p1.v - p0.v;
  163.             v2h = p2.h - p1.h;
  164.             v2v = p2.v - p1.v;
  165.  
  166.             /* Check for backfaced polygon by doing cross-product.  Skip if backfaced.
  167.              * Try using '<=' test instead to get the inside-out effect */
  168.             if ((v1h * v2v - v1v * v2h) >= 0) continue;
  169.         }
  170.  
  171.         {
  172.             /* Draw the polygon */
  173.             
  174.             PolyHandle    polyHdl = nil;
  175.             Integer j;
  176.             Integer vertexIndex;
  177.  
  178.             if (gRenderingStyle != iLineDrawing) {
  179.                 /* gRenderingStyle is iFlatShading or iFlatShadingWithOutline.
  180.                  * Begin collecting QD polygon */
  181.                 polyHdl = OpenPoly ();
  182.             }
  183.             PolyColor (&polyNet->fColor [thisPoly->fColorIndex]);
  184.             
  185.             /* Initial move */
  186.             vertexIndex = vertexIndices [firstVertexIndex];
  187.             MoveTo3D    (    vertices [vertexIndex].x,
  188.                             vertices [vertexIndex].y,
  189.                             vertices [vertexIndex].z);
  190.             /* Hit the vertices */                
  191.             for (j = firstVertexIndex+1; j < firstVertexIndex+thisPoly->fVertexCount; j++){
  192.                 vertexIndex = vertexIndices [j];
  193.                 LineTo3D(    vertices [vertexIndex].x,
  194.                             vertices [vertexIndex].y,
  195.                             vertices [vertexIndex].z);
  196.             }
  197.             /* Close the polygon */
  198.             vertexIndex = vertexIndices [firstVertexIndex];
  199.             LineTo3D    (    vertices [vertexIndex].x,
  200.                             vertices [vertexIndex].y,
  201.                             vertices [vertexIndex].z);
  202.             
  203.             if (gRenderingStyle != iLineDrawing) {
  204.                 ClosePoly ();                                  /* Stop collecting QD polygon */
  205.                 FillPoly (polyHdl, lgPolyShade);
  206.                 if (gRenderingStyle == iFlatShadingWithOutline) {
  207.                     ForeColor (blackColor);
  208.                     FramePoly (polyHdl);
  209.                 }
  210.                 KillPoly (polyHdl);
  211.             }
  212.         }
  213.     }
  214. }
  215.  
  216. /*=================================================================================================
  217. /* Length3D
  218. /*
  219. /* Returns the length of vector a 
  220. /*-------------------------------------------------------------------------------------------------*/
  221. pascal Real Length3D (const CPoint3D *a)
  222. {
  223.     return (sqrt (a->x*a->x + a->y*a->y + a->z*a->z));
  224. }
  225.  
  226. /*=================================================================================================
  227. /* Matrix2XfMatrix
  228. /*
  229. /* Converts a Matrix4D to a XfMatrix used by Graf3D
  230. /*-------------------------------------------------------------------------------------------------*/
  231. pascal void Matrix2XfMatrix (Matrix4D fromMatrix, XfMatrix toMatrix)
  232. {
  233.     Integer i, j;
  234.     
  235.     for (i=3; i>=0; i--) {
  236.         for (j=3; j>=0; j--) {
  237.             toMatrix[i][j]= Real2Fix (fromMatrix[i][j]);
  238.         }
  239.     }
  240.  
  241. #ifdef xxxxxxxxxxxxxxxxxxxxxxxxxNOT_USEDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  242.     /* Faster but kludgy version of the same thing */
  243.     
  244.     Real    *fromPtr = (Real  *) fromMatrix;
  245.     Fixed    *toPtr   = (Fixed *) toMatrix;
  246.     Integer i;
  247.     for (i=15; i>=0; i--) {
  248.         *toPtr++ = Real2Fix (*fromPtr++);
  249.     }
  250. #endif xxxxxxxxxxxxxxxxxxxxxxxxxNOT_USEDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  251. }
  252.  
  253. /*=================================================================================================
  254. /* MultiplyMatrix
  255. /*
  256. /* Matrix multiplication c = a x b 
  257. /*-------------------------------------------------------------------------------------------------*/
  258. pascal void MultiplyMatrix (Matrix4D a, Matrix4D b, Matrix4D aTimesB)
  259. {
  260.     Integer i, j, k;
  261.  
  262.     for (i=3; i>=0; i--) {
  263.         for (j=3; j>=0; j--) {
  264.             Real sum = 0.0;
  265.             for (k=3; k>=0; k--) {
  266.                 sum += a[i][k] * b[k][j];
  267.             }
  268.             aTimesB[i][j]= sum;
  269.         }
  270.     }
  271. }
  272.  
  273. /*=================================================================================================
  274. /* Normalize3D
  275. /*
  276. /* Returns the normalized vector
  277. /*-------------------------------------------------------------------------------------------------*/
  278. pascal void Normalize3D (CPoint3D *v)
  279. {
  280.     Real length;
  281.  
  282.     length = sqrt (v->x * v->x + v->y * v->y + v->z * v->z);
  283.     if (length > 0) {
  284.         v->x /= length;
  285.         v->y /= length;
  286.         v->z /= length;
  287.     } else {
  288.         /* Vector is zero.  Probably should set some error */
  289.         v->x = v->y = v->z = 0;
  290.         DebugMessage ("\p Normalize3D: zero vector!");
  291.     }
  292. }
  293.  
  294. /*=================================================================================================
  295. /* OrthogonalizeRotationMatrix
  296. /*
  297. /* Orthogonalizes a pure rotation matrix (this is not checked!).
  298. /* Assumes the "y-axis" (2nd row) of the matrix is "correct"
  299. /* and make 1st and 3rd row orthogonal to it.
  300. /* Assumes matrix is already close to orthogonal.
  301. /*-------------------------------------------------------------------------------------------------*/
  302. pascal void OrthogonalizeRotationMatrix (Matrix4D matrix)
  303. {
  304.     CPoint3D xAxis, yAxis, zAxis;
  305.  
  306.     yAxis.x = matrix[1][0];
  307.     yAxis.y = matrix[1][1];
  308.     yAxis.z = matrix[1][2];
  309.     zAxis.x = matrix[2][0];
  310.     zAxis.y = matrix[2][1];
  311.     zAxis.z = matrix[2][2];
  312.  
  313.     Normalize3D (&yAxis);
  314.     UnitCrossProduct3D  (&yAxis, &zAxis, &xAxis);
  315.     UnitCrossProduct3D  (&xAxis, &yAxis, &zAxis);
  316.  
  317.     matrix[0][0] = xAxis.x;
  318.     matrix[0][1] = xAxis.y;
  319.     matrix[0][2] = xAxis.z;
  320.  
  321.     matrix[1][0] = yAxis.x;
  322.     matrix[1][1] = yAxis.y;
  323.     matrix[1][2] = yAxis.z;
  324.  
  325.     matrix[2][0] = zAxis.x;
  326.     matrix[2][1] = zAxis.y;
  327.     matrix[2][2] = zAxis.z;
  328. }
  329.  
  330. /*=================================================================================================
  331. /* PolyColor
  332. /*
  333. /* Given a RGB color, calls the appropriate QuickDraw routines to set up the
  334. /* appropriate drawing environment.  If gDrawInColor is false (because of a
  335. /* menu choice or color QD is not available) the color is replaced by a
  336. /* dithering pattern. 
  337. /*-------------------------------------------------------------------------------------------------*/
  338. pascal void PolyColor (const RGBColor *rGBColor)
  339. {
  340.     if (gDrawInColor) {
  341.         lgPolyShade = qd.black;
  342.         RGBForeColor (rGBColor);
  343.     } else {
  344.         /* Convert rGBColor to a dither pattern. */
  345.         unsigned long index;
  346.         index = RGBToGrayscale (rGBColor, (**lgDitherPatterns).patListSize);
  347.         lgPolyShade = (**lgDitherPatterns).patList [index];
  348.         ForeColor (blackColor);
  349.     }
  350. }
  351.  
  352. /*=================================================================================================
  353. /* RGBToGrayscale
  354. /*
  355. /* Given a RGB color, convert it to a grayscale value from 0 to (maxGrayValue-1). 
  356. /*-------------------------------------------------------------------------------------------------*/
  357. static unsigned long RGBToGrayscale (const RGBColor *rGBColor, Integer maxGrayValue)
  358. {
  359.     #define    kRWeight            3
  360.     #define    kGWeight            6
  361.     #define    kBWeight            1
  362.     #define    kTotalWeight    (kRWeight + kGWeight + kBWeight)
  363.     
  364.     unsigned long intensity;
  365.     unsigned long index;
  366.     intensity = kRWeight*rGBColor->red + kGWeight*rGBColor->green +
  367.                 kBWeight*rGBColor->blue;
  368.     index = intensity * maxGrayValue / kTotalWeight / (USHRT_MAX+1);
  369.                                     /* Note integer math. Order matters */
  370.     return (index);
  371. }
  372.  
  373. /*=================================================================================================
  374. /* SetRotationMatrix
  375. /*
  376. /* Computes a rotation matrix that would map (rotate) vectors op onto oq.
  377. /* The rotation is about an axis perpendicular to op and oq.
  378. /* Note this routine won't work if op or oq are zero vectors, or if they
  379. /* are parallel or antiparallel to each other.  Note the implementation below
  380. /* is easy to read but is not done in the most efficient way.  It can be
  381. /* sped up by reusing some of the temporary results.
  382. /*
  383. /* Modification of Michael Pique's formula in 
  384. /* Graphics Gems Vol. 1.  Andrew Glassner, Ed.  Addison-Wesley.
  385. /*-------------------------------------------------------------------------------------------------*/
  386. pascal void SetRotationMatrix (Matrix4D rotationMatrix, const CPoint3D *op, const CPoint3D *oq)
  387. {
  388.     Real        s, c, t;
  389.     CPoint3D    a;
  390.     
  391.     #define ax    (a.x)
  392.     #define ay    (a.y)
  393.     #define az    (a.z)
  394.     #define ax2    (ax * ax)
  395.     #define ay2    (ay * ay)
  396.     #define az2    (az * az)
  397.  
  398.     CrossProduct3D (op, oq, &a);
  399.     s = Length3D (&a);
  400.     c = DotProduct3D (op, oq);
  401.     t = 1 - c;
  402.  
  403.     if (s > 0) {
  404.         a.x /= s;
  405.         a.y /= s;
  406.         a.z /= s;
  407.     }
  408.  
  409.     rotationMatrix[0][0] = t*ax2+c;
  410.     rotationMatrix[0][1] = t*ax*ay+s*az;
  411.     rotationMatrix[0][2] = t*ax*az-s*ay;
  412.  
  413.     rotationMatrix[1][0] = t*ax*ay-s*az;
  414.     rotationMatrix[1][1] = t*ay2+c;
  415.     rotationMatrix[1][2] = t*ay*az+s*ax;
  416.  
  417.     rotationMatrix[2][0] = t*ax*az+s*ay;
  418.     rotationMatrix[2][1] = t*ay*az-s*ax;
  419.     rotationMatrix[2][2] = t*az2+c;
  420.  
  421.     rotationMatrix[0][3] = rotationMatrix[1][3] = rotationMatrix[2][3] = 
  422.     rotationMatrix[3][0] = rotationMatrix[3][1] = rotationMatrix[3][2] = 0.0;
  423.     rotationMatrix[3][3] = 1.0;
  424.  
  425.     #undef ax
  426.     #undef ay
  427.     #undef az
  428.     #undef ax2
  429.     #undef ay2
  430.     #undef az2
  431. }
  432.  
  433. /*=================================================================================================
  434. /* UnitCrossProduct3D
  435. /*
  436. /* Returns the normalized right-handed cross product of a and b in c
  437. /*-------------------------------------------------------------------------------------------------*/
  438. pascal void UnitCrossProduct3D (const CPoint3D *a, const CPoint3D *b, CPoint3D *aCrossB)
  439. {
  440.     CrossProduct3D (a, b, aCrossB);
  441.     Normalize3D (aCrossB);
  442. }
  443.  
  444.